/*
* Conditions Of Use 
* 
* This software was developed by employees of the National Institute of
* Standards and Technology (NIST), an agency of the Federal Government.
* Pursuant to title 15 Untied States Code Section 105, works of NIST
* employees are not subject to copyright protection in the United States
* and are considered to be in the public domain.  As a result, a formal
* license is not needed to use the software.
* 
* This software is provided by NIST as a service and is expressly
* provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  NIST does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
* 
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement
*  
* .
* 
*/
/*******************************************************************************
* Product of NIST/ITL Advanced Networking Technologies Division (ANTD).        *
*******************************************************************************/
package gov.nist.javax.sdp.fields;
import gov.nist.core.*;
import java.lang.reflect.*;

/**
* Root class for everything in this package.
*/
public abstract class SDPObject
	extends GenericObject
	implements SDPFieldNames {

	protected static final String SDPFIELDS_PACKAGE =
		PackageNames.SDP_PACKAGE + ".fields";
	protected static final String CORE_PACKAGE = PackageNames.CORE_PACKAGE;

	// should make this a property during the compile.

	public abstract String encode();

	protected void sprint(String s) {
		super.sprint(s);
	}

	protected void initSprint() {
		super.stringRepresentation = "";
	}

	public String toString() {
		return encode();
	}

	protected String getStringRepresentation() {
		return super.stringRepresentation;
	}

	/**
	* An introspection based equality predicate for SDPObjects.
	*@param  that is the other object to test against.
	*@return true if the objects are equal.
	*/
	public boolean equals(Object that) {
		if (!this.getClass().equals(that.getClass()))
			return false;
		Class myclass = this.getClass();
		Field[] fields = myclass.getDeclaredFields();
		Class hisclass = that.getClass();
		Field[] hisfields = hisclass.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field f = fields[i];
			Field g = hisfields[i];
			// Only print protected and public members.
			int modifier = f.getModifiers();
			if (modifier == Modifier.PRIVATE)
				continue;
			Class fieldType = f.getType();
			String fieldName = f.getName();
			if (fieldName.compareTo("stringRepresentation") == 0) {
				continue;
			}
			if (fieldName.compareTo("indentation") == 0) {
				continue;
			}
			try {
				// Primitive fields are printed with type: value 
				if (fieldType.isPrimitive()) {
					String fname = fieldType.toString();
					if (fname.compareTo("int") == 0) {
						if (f.getInt(this) != g.getInt(that))
							return false;
					} else if (fname.compareTo("short") == 0) {
						if (f.getShort(this) != g.getShort(that))
							return false;
					} else if (fname.compareTo("char") == 0) {
						if (f.getChar(this) != g.getChar(that))
							return false;
					} else if (fname.compareTo("long") == 0) {
						if (f.getLong(this) != g.getLong(that))
							return false;
					} else if (fname.compareTo("boolean") == 0) {
						if (f.getBoolean(this) != g.getBoolean(that))
							return false;
					} else if (fname.compareTo("double") == 0) {
						if (f.getDouble(this) != g.getDouble(that))
							return false;
					} else if (fname.compareTo("float") == 0) {
						if (f.getFloat(this) != g.getFloat(that))
							return false;
					}
				} else if (g.get(that) == f.get(this))
					continue;
				else if (f.get(this) == null && g.get(that) != null)
					return false;
				else if (g.get(that) == null && f.get(that) != null)
					return false;
				else if (!f.get(this).equals(g.get(that)))
					return false;
			} catch (IllegalAccessException ex1) {
				InternalErrorHandler.handleException(ex1);
			}
		}
		return true;
	}

	/**
	* Generic introspection based printing funciton for this package.
	* Because we use introspection, this must reside in this package
	* and we cannot use the superclass defintion (even though it 
	* is virtually identical.
	*/
	public String debugDump() {
		stringRepresentation = "";
		Class myclass = getClass();
		sprint(myclass.getName());
		sprint("{");
		Field[] fields = myclass.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field f = fields[i];
			// Only print protected and public members.
			int modifier = f.getModifiers();
			if (modifier == Modifier.PRIVATE)
				continue;
			Class fieldType = f.getType();
			String fieldName = f.getName();
			if (fieldName.compareTo("stringRepresentation") == 0) {
				// avoid nasty recursions...
				continue;
			}
			if (fieldName.compareTo("indentation") == 0) {
				// formatting stuff - not relevant here.
				continue;
			}
			sprint(fieldName + ":");
			try {
				// Primitive fields are printed with type: value 
				if (fieldType.isPrimitive()) {
					String fname = fieldType.toString();
					sprint(fname + ":");
					if (fname.compareTo("int") == 0) {
						int intfield = f.getInt(this);
						sprint(intfield);
					} else if (fname.compareTo("short") == 0) {
						short shortField = f.getShort(this);
						sprint(shortField);
					} else if (fname.compareTo("char") == 0) {
						char charField = f.getChar(this);
						sprint(charField);
					} else if (fname.compareTo("long") == 0) {
						long longField = f.getLong(this);
						sprint(longField);
					} else if (fname.compareTo("boolean") == 0) {
						boolean booleanField = f.getBoolean(this);
						sprint(booleanField);
					} else if (fname.compareTo("double") == 0) {
						double doubleField = f.getDouble(this);
						sprint(doubleField);
					} else if (fname.compareTo("float") == 0) {
						float floatField = f.getFloat(this);
						sprint(floatField);
					}
				} else if (
					Class.forName(
						CORE_PACKAGE + ".GenericObject").isAssignableFrom(
						fieldType)) {
					if (f.get(this) != null) {
						sprint(
							((GenericObject) f.get(this)).debugDump(
								indentation + 1));
					} else {
						sprint("<null>");
					}

				} else if (
					Class.forName(
						CORE_PACKAGE + ".GenericObjectList").isAssignableFrom(
						fieldType)) {
					if (f.get(this) != null) {
						sprint(
							((GenericObjectList) f.get(this)).debugDump(
								indentation + 1));
					} else {
						sprint("<null>");
					}

				} else {
					// Dont do recursion on things that are not
					// of our header type...
					if (f.get(this) != null) {
						sprint(f.get(this).getClass().getName() + ":");
					} else {
						sprint(fieldType.getName() + ":");
					}

					sprint("{");
					if (f.get(this) != null) {
						sprint(f.get(this).toString());
					} else {
						sprint("<null>");
					}
					sprint("}");
				}
			} catch (IllegalAccessException ex1) {
				continue; // we are accessing a private field...
			} catch (ClassNotFoundException ex) {
				System.out.println("Cound not find " + ex.getMessage());
				ex.printStackTrace();
				System.exit(0);
			}
		}
		sprint("}");
		return stringRepresentation;
	}

	/** An introspection based predicate matching using a template
	* object. Allows for partial match of two protocl Objects. 
	*@param other the match pattern to test against. The match object
	* has to be of the same type (class). Primitive types 
	* and non-sip fields that are non null are matched for equality.
	* Null in any field  matches anything. Some book-keeping fields
	* are ignored when making the comparison.
	*/

	public boolean match(Object other) {
		if (other == null)
			return true;
		if (!this.getClass().equals(other.getClass()))
			return false;
		GenericObject that = (GenericObject) other;
		Class myclass = this.getClass();
		Field[] fields = myclass.getDeclaredFields();
		Class hisclass = other.getClass();
		Field[] hisfields = hisclass.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field f = fields[i];
			Field g = hisfields[i];
			// Only print protected and public members.
			int modifier = f.getModifiers();
			if (modifier == Modifier.PRIVATE)
				continue;
			Class fieldType = f.getType();
			String fieldName = f.getName();
			if (fieldName.compareTo("stringRepresentation") == 0) {
				continue;
			}
			if (fieldName.compareTo("indentation") == 0) {
				continue;
			}
			try {
				// Primitive fields are printed with type: value 
				if (fieldType.isPrimitive()) {
					String fname = fieldType.toString();
					if (fname.compareTo("int") == 0) {
						if (f.getInt(this) != g.getInt(that))
							return false;
					} else if (fname.compareTo("short") == 0) {
						if (f.getShort(this) != g.getShort(that))
							return false;
					} else if (fname.compareTo("char") == 0) {
						if (f.getChar(this) != g.getChar(that))
							return false;
					} else if (fname.compareTo("long") == 0) {
						if (f.getLong(this) != g.getLong(that))
							return false;
					} else if (fname.compareTo("boolean") == 0) {
						if (f.getBoolean(this) != g.getBoolean(that))
							return false;
					} else if (fname.compareTo("double") == 0) {
						if (f.getDouble(this) != g.getDouble(that))
							return false;
					} else if (fname.compareTo("float") == 0) {
						if (f.getFloat(this) != g.getFloat(that))
							return false;
					}
				} else {
					Object myObj = f.get(this);
					Object hisObj = g.get(that);
					if (hisObj == myObj)
						return true;
					else if (hisObj != null && myObj == null)
						return false;
					else if (
						hisObj instanceof java.lang.String
							&& myObj instanceof java.lang.String) {
						if (((String) myObj)
							.compareToIgnoreCase((String) hisObj)
							!= 0)
							return false;
					} else if (
						hisObj != null
							&& GenericObject.isMySubclass(myObj.getClass())
							&& GenericObject.isMySubclass(hisObj.getClass())
							&& myObj.getClass().equals(hisObj.getClass())
							&& ((GenericObject) hisObj).getMatcher() != null) {
						String myObjEncoded = ((GenericObject) myObj).encode();
						return ((GenericObject) hisObj).getMatcher().match(
							myObjEncoded);
					} else if (
						GenericObject.isMySubclass(myObj.getClass())
							&& !((GenericObject) myObj).match(hisObj))
						return false;
					else if (
						GenericObjectList.isMySubclass(myObj.getClass())
							&& !((GenericObjectList) myObj).match(hisObj))
						return false;
				}
			} catch (IllegalAccessException ex1) {
				InternalErrorHandler.handleException(ex1);
			}
		}
		return true;
	}

	
	/**
	* Formatter with a given starting indentation.
	*/
	public String dbgPrint(int indent) {
		int save = indentation;
		indentation = indent;
		String retval = this.toString();
		indentation = save;
		return retval;
	}

	
}

